Imports Phoenix.DataAccess
Imports Phoenix.Events
Imports Phoenix.Forms.Dialogs

Imports ComponentGo.Calendars

Namespace Helpers


#Region " AppointmentStatus enum "
    Public Enum AppointmentStatus
        asNew
        asScheduled
        asOpen
        asPaid
    End Enum
#End Region

    Public Class AppointmentHelper : Inherits BaseHelper

#Region " BaseHelper Overrides "
        Private Shared mInstance = New AppointmentHelper
        Public Shared ReadOnly Property Instance() As AppointmentHelper
            Get
                Return mInstance
            End Get
        End Property


        Private Sub New()

        End Sub

#End Region

#Region " CRUD Methods "

        Private Shared Sub UpdateAppointmentCore(ByVal Appointment As DataSetAppointments.Appointment)
            If Appointment.RowState = DataRowState.Detached Then
                Appointment.CreationTime = Now()
                CType(Appointment.Table, DataAccess.DataSetAppointments.AppointmentsDataTable).AddAppointment(Appointment)
            End If

            AppDataLayer.UpdateAppointment(Appointment)
        End Sub

        Private Shared Sub DeleteAppointmentCore(ByVal Appointment As DataSetAppointments.Appointment)
            Appointment.Delete()
            AppDataLayer.UpdateAppointment(Appointment)
        End Sub

#End Region

#Region " Edit commands "

        Public Shared Function NewAppointment() As DataSetAppointments.Appointment
            Return NewAppointment(Now)
        End Function

        Public Shared Function NewAppointment(ByVal service As DataSetData.Service) As DataSetAppointments.Appointment
            Dim dba As DataSetAppointments.Appointment = NewAppointment(Now)
            dba.ServiceID = service.ServiceID
            dba.Description = service.Label
            Return dba
        End Function

        Public Shared Function NewAppointment(ByVal d As Date) As DataSetAppointments.Appointment
            Return NewAppointment(New DataSetAppointments, d)
        End Function

        Public Shared Function NewAppointment(ByVal dsApp As DataSetAppointments) As DataSetAppointments.Appointment
            Return NewAppointment(dsApp, Now)
        End Function

        Public Shared Function NewAppointment(ByVal dsApp As DataSetAppointments, ByVal d As Date) As DataSetAppointments.Appointment
            Dim app As DataSetAppointments.Appointment
            If dsApp Is Nothing Then
                dsApp = New DataSetAppointments
            End If
            app = dsApp.Appointments.NewAppointment
            app.OperatorID = Security.CurrentUser.EmployeeID
            app.StartTime = d
            app.FinishTime = app.StartTime
            app.ExpectedDuration = 0
            Return app
        End Function

        Public Shared Function Copy(ByVal obj As DataSetAppointments.Appointment) As DataSetAppointments.Appointment
            Dim s As DataSetAppointments.Appointment = NewAppointment()
            Copy(s, obj)
            s.Description = s.Description
            Return s
        End Function


        Public Shared Sub Copy(ByVal app As DataAccess.DataSetAppointments.Appointment, ByVal nApp As DataAccess.DataSetAppointments.Appointment)
            app.ClientID = nApp.ClientID
            app.Comment = nApp.Comment
            app.Description = nApp.Description
            app.ExpectedDuration = nApp.ExpectedDuration
            app.OperatorID = nApp.OperatorID
            app.PerformerID = nApp.PerformerID
            app.ServiceCost = nApp.ServiceCost
            app.ServiceID = nApp.ServiceID
        End Sub

        Public Shared Sub FullCopy(ByVal app As DataAccess.DataSetAppointments.Appointment, ByVal nApp As DataAccess.DataSetAppointments.Appointment)
            Copy(app, nApp)
            If nApp.IsBillIDNull Then
                app.SetBillIDNull()
            Else
                app.BillID = nApp.BillID
            End If
            app.CreationTime = nApp.CreationTime
            app.FinishTime = nApp.FinishTime
            app.StartTime = nApp.StartTime
        End Sub


        Public Shared Function CheckAndUpdateAppointment(ByVal app As DataSetAppointments.Appointment, ByVal sender As Object)
            Dim start As DateTime = app.StartTime
            Dim finish As DateTime = app.FinishTime

            If Not IsNotPastTime(app) Then
                AppMessageBox(Res.AppointmentBookInPastError, MsgBoxStyle.Critical)
                Return False
            End If
            If Not IsValidSalonTime(app) Then
                AppMessageBox(Res.AppointmentNotInSalonTime, MsgBoxStyle.Critical)
                Return False
            End If
            If Not IsValidEmployeeTime(app) Then
                AppMessageBox(Res.AppointmentNotInStaffMemberFreeTime, MsgBoxStyle.Critical)
                Return False
            End If
            If Not IsValidClientTime(app) Then
                AppMessageBox(Res.AppointmentNotInClientFreeTime, MsgBoxStyle.Critical)
                Return False
            End If
            If app.StartTime.CompareTo(start) <> 0 Or app.FinishTime.CompareTo(finish) <> 0 Then
                If Not AskToSqueezeAppointment(start, finish, app) Then
                    Return False
                End If
            End If
            UpdateAppointment(app, sender)
            Return True
        End Function


        Public Shared Sub UpdateAppointment(ByVal dba As DataSetAppointments.Appointment, ByVal sender As Object)
            Dim e As PhoenixEventArgs

            If dba.RowState = DataRowState.Detached Then
                e = New PhoenixEventArgs(dba, PhoenixEventCode.BizAdded)
            Else
                e = New PhoenixEventArgs(dba, PhoenixEventCode.BizUpdated)
            End If

            UpdateAppointmentCore(dba)

            AppRaiseEvent(sender, e)
        End Sub


        Public Shared Sub DeleteAppointment(ByVal obj As DataSetAppointments.Appointment, ByVal sender As Object)
            DeleteAppointment(obj, sender, True)
        End Sub

        Public Shared Sub DeleteAppointment(ByVal obj As DataSetAppointments.Appointment, ByVal sender As Object, ByVal confirm As Boolean)
            If CloseForm(obj) Then
                If confirm Then
                    If Not AppConfig.SilenceAppointmentDeletion Then
                        Dim result As MsgBoxResult = AppMessageBox(String.Format(Res.AppointmentDeleteQuestion, GetAppointmentLabel(obj)), MsgBoxStyle.YesNo Or MsgBoxStyle.Exclamation)
                        If result <> MsgBoxResult.Yes Then Return
                    End If
                End If

                AppRaiseEvent(sender, New PhoenixEventArgs(obj, PhoenixEventCode.BizRemoved))
                Try
                    DeleteAppointmentCore(obj)
                Catch
                    AppRaiseEvent(sender, New PhoenixEventArgs(obj, PhoenixEventCode.BizAdded))
                End Try
            End If
        End Sub

        Public Shared Sub DeleteAppointments(ByVal list As IList, ByVal sender As Object)
            If list.Count = 1 Then
                DeleteAppointment(list(0), sender, True)
            Else
                DeleteAppointments(list, sender, True)
            End If
        End Sub

        Public Shared Sub DeleteAppointments(ByVal list As IList, ByVal sender As Object, ByVal confirm As Boolean)
            If (Not AppConfig.SilenceAppointmentsDeletion) And confirm Then
                Dim result As MsgBoxResult = AppMessageBox(String.Format(Res.AppointmentsDeleteQuestion, list.Count), MsgBoxStyle.YesNo Or MsgBoxStyle.Exclamation)
                If result <> MsgBoxResult.Yes Then Return
            End If

            OperationCanceled = False
            Dim mylist(list.Count - 1) As DataSetAppointments.Appointment
            list.CopyTo(mylist, 0)
            For i As Long = 0 To list.Count - 1
                DeleteAppointment(mylist(i), sender, False)
                If OperationCanceled Then Return
            Next
        End Sub



#End Region

#Region " Check Appointment "

        Public Shared Function AskToSqueezeAppointment(ByVal start As Date, ByVal finish As Date, ByVal nApp As DataAccess.DataSetAppointments.Appointment) As Boolean
            Return AppMessageBox(String.Format(Res.SqueezeAppointmentMessage, _
                finish.Subtract(start).TotalMinutes, _
                nApp.FinishTime.Subtract(nApp.StartTime).TotalMinutes), _
                MsgBoxStyle.Exclamation Or MsgBoxStyle.YesNo) = MsgBoxResult.Yes
        End Function

        Public Shared Function IsValidTime(ByVal app As DataSetAppointments.Appointment) As Boolean
            Return IsValidTime(app.StartTime, app.FinishTime, app)
        End Function

        Public Shared Function IsValidTime(ByVal start As DateTime, ByVal finish As DateTime, ByVal app As DataSetAppointments.Appointment) As Boolean
            Return IsValidTime(start, finish, app, False)
        End Function

        Public Shared Function IsValidTime(ByVal start As DateTime, ByVal finish As DateTime, ByVal app As DataSetAppointments.Appointment, ByVal NoSqueeze As Boolean) As Boolean
            If Not IsNotPastTime(app) Then
                Return False
            End If
            If Not IsValidSalonTime(app) Then
                Return False
            End If
            If Not IsValidEmployeeTime(app) Then
                Return False
            End If
            If Not IsValidClientTime(app) Then
                Return False
            End If
            If app.StartTime.CompareTo(start) <> 0 Or app.FinishTime.CompareTo(finish) <> 0 Then
                If Not NoSqueeze Then
                    Return AskToSqueezeAppointment(start, finish, app)
                Else
                    Return False
                End If
            Else
                Return True
            End If
        End Function

        Public Shared Function IsValidSalonTime(ByVal app As DataSetAppointments.Appointment) As Boolean
            Return IsValidSalonTime(app, False)
        End Function

        Public Shared Function IsNotPastTime(ByVal app As DataSetAppointments.Appointment) As Boolean
            Return IsNotPastTime(app.StartTime.Date)
        End Function

        Public Shared Function IsNotPastTime(ByVal d As DateTime) As Boolean
            If DateTime.Compare(d.Date, Now.Date) < 0 Then
                Return False
            Else
                Return True
            End If
        End Function

        Public Shared Function IsValidSalonTime(ByVal app As DataSetAppointments.Appointment, ByVal checkOnly As Boolean) As Boolean
            For Each st As DataSetData.SalonTime In AppDataLayer.SalonTimes
                If st.RowState <> DataRowState.Deleted Then
                    If st.DayOfWeek = app.StartTime.DayOfWeek Then
                        Dim start As Integer = app.StartTime.TimeOfDay.TotalMinutes
                        Dim finish As Integer = app.FinishTime.TimeOfDay.TotalMinutes
                        GetSubAppointment(st.StartTime, st.FinishTime, start, finish, False)
                        If (start <> 0) And (finish <> 0) Then
                            Return ChangeAppPeriod(start, finish, app, checkOnly)
                        End If
                    End If
                End If
            Next
            Return False
        End Function

        Public Shared Function IsValidEmployeeTime(ByVal app As DataSetAppointments.Appointment) As Boolean
            Return IsValidEmployeeTime(app, False)
        End Function

        Public Shared Function IsValidEmployeeTime(ByVal app As DataSetAppointments.Appointment, ByVal checkOnly As Boolean) As Boolean
            Dim start As Integer = app.StartTime.TimeOfDay.TotalMinutes
            Dim finish As Integer = app.FinishTime.TimeOfDay.TotalMinutes

            Dim bm As BreakManager = New BreakManager(True)

            For Each et As DataSetData.EmployeeTime In AppDataLayer.EmployeeTimes
                If et.RowState <> DataRowState.Deleted Then
                    If et.EmployeeID = app.PerformerID Then
                        If et.DayOfWeek = app.StartTime.DayOfWeek Then
                            bm.AddBreak(TimeSpan.FromMinutes(et.StartTime), TimeSpan.FromMinutes(et.FinishTime))
                        End If
                    End If
                End If
            Next

            For Each bt As DataSetAppointments.BusyTime In AppDataLayer.GetBusyTimes(app.PerformerID, app.StartTime, app.FinishTime).BusyTimes
                bm.AddBreak(TimeSpan.FromMinutes(bt.StartTime), TimeSpan.FromMinutes(bt.FinishTime))
            Next

            For Each bt As DataSetAppointments.BusyTime In AppDataLayer.GetAdditionalTimes(app.PerformerID, app.StartTime.Date, app.StartTime.Date.AddDays(1)).BusyTimes
                bm.RemoveBreak(TimeSpan.FromMinutes(bt.StartTime), TimeSpan.FromMinutes(bt.FinishTime))
            Next

            For Each b As Break In bm.Breaks
                GetSubAppointment(b.BeginBreak.TotalMinutes, b.EndBreak.TotalMinutes, start, finish, True)
            Next
            If Not ChangeAppPeriod(start, finish, app, checkOnly) Then Return False


            For Each cApp As DataSetAppointments.Appointment In AppDataLayer.GetEmployeeAppointments(app.PerformerID, app.StartTime.Date).Appointments
                If app.AppointmentID <> cApp.AppointmentID Then
                    GetSubAppointment(cApp.StartTime.TimeOfDay.TotalMinutes, cApp.FinishTime.TimeOfDay.TotalMinutes, start, finish, True)
                End If
            Next
            If Not ChangeAppPeriod(start, finish, app, checkOnly) Then Return False

            Return True
        End Function

        Public Shared Function IsValidClientTime(ByVal app As DataSetAppointments.Appointment) As Boolean
            Return IsValidClientTime(app, False)
        End Function

        Public Shared Function IsValidClientTime(ByVal app As DataSetAppointments.Appointment, ByVal checkOnly As Boolean) As Boolean
            If app.ClientID = 0 Then Return True

            Dim start As Integer = app.StartTime.TimeOfDay.TotalMinutes
            Dim finish As Integer = app.FinishTime.TimeOfDay.TotalMinutes

            Dim dsClientAppointments As DataSetAppointments = AppDataLayer.GetClientAppointments(app.ClientID, app.StartTime, app.FinishTime)
            For Each cApp As DataSetAppointments.Appointment In dsClientAppointments.Appointments
                If app.AppointmentID <> cApp.AppointmentID Then
                    GetSubAppointment(cApp.StartTime.TimeOfDay.TotalMinutes, cApp.FinishTime.TimeOfDay.TotalMinutes, start, finish, True)
                End If

            Next
            Return ChangeAppPeriod(start, finish, app, checkOnly)
        End Function
#End Region

#Region " Others Helpers "

        Public Shared Function ExpectedServiceDuration(ByVal app As DataSetAppointments.Appointment) As Integer
            If app.IsServiceIDNull Then Return 0
            If app.IsPerformerIDNull Then Return 0

            Dim level As Integer = AppDataLayer.Employees.FindByEmployeeID(app.PerformerID).MasterLevelID

            For Each sp As DataSetData.ServiceParam In AppDataLayer.Services.FindByServiceID(app.ServiceID).GetServiceParams()
                If sp.MasterLevelID = level Then
                    Return sp.Duration
                End If
            Next
            Return 0
        End Function


        Public Shared Function ExpectedServiceCost(ByVal app As DataSetAppointments.Appointment) As Decimal
            If app.IsServiceIDNull Then Return 0
            If app.IsPerformerIDNull Then Return 0

            Dim level As Integer = AppDataLayer.Employees.FindByEmployeeID(app.PerformerID).MasterLevelID

            For Each sp As DataSetData.ServiceParam In AppDataLayer.Services.FindByServiceID(app.ServiceID).GetServiceParams()
                If sp.MasterLevelID = level Then
                    Return sp.Price
                End If
            Next
            Return 0
        End Function

        Public Shared Function GetAppointmentLabel(ByVal app As DataSetAppointments.Appointment) As String
            If GetAppointmentStatus(app) = AppointmentStatus.asNew Then Return Res.NewAppointmentLabel

            If app.Description.Trim() = String.Empty Then
                Return String.Format("{1} ({0})", _
                    AppDataLayer.Services.FindByServiceID(app.ServiceID).Label, _
                    AppDataLayer.Clients.FindByClientID(app.ClientID).FullName)
            Else
                Return app.Description
            End If
        End Function

        Public Shared Function GetAppointmentTooltip(ByVal dba As DataSetAppointments.Appointment) As String
            If AppointmentHelper.GetAppointmentStatus(dba) = AppointmentStatus.asNew Then Return Res.NewAppointmentTooltip

            Return String.Format("{0:t} - {1} ({2})", _
                dba.StartTime, _
                AppDataLayer.Clients.FindByClientID(dba.ClientID).FullName, _
                AppDataLayer.Services.FindByServiceID(dba.ServiceID).Label)
        End Function

        Public Shared Function GetAppointmentBackColor(ByVal app As DataSetAppointments.Appointment) As Color
            Select Case GetAppointmentStatus(app)
                Case AppointmentStatus.asNew
                    Return AppConfig.NewAppointmentBackColor
                Case AppointmentStatus.asOpen
                    Return AppConfig.OpenAppointmentBackColor
                Case AppointmentStatus.asPaid
                    Return AppConfig.PaidAppointmentBackColor
                Case Else
                    Dim s As DataSetData.Service = AppDataLayer.Services.FindByServiceID(app.ServiceID)
                    If Not s Is Nothing Then
                        If s.Color <> 0 Then
                            Return Color.FromArgb(s.Color)
                        End If
                    End If
                    Return AppConfig.ScheduleAppointmentBackColor
            End Select
        End Function


        Public Shared Function GetAppointmentForeColor(ByVal app As DataSetAppointments.Appointment) As Color
            Select Case GetAppointmentStatus(app)
                Case AppointmentStatus.asNew
                    Return AppConfig.NewAppointmentForeColor
                Case AppointmentStatus.asOpen
                    Return AppConfig.OpenAppointmentForeColor
                Case AppointmentStatus.asPaid
                    Return AppConfig.PaidAppointmentForeColor
                Case Else
                    Dim s As DataSetData.Service = AppDataLayer.Services.FindByServiceID(app.ServiceID)
                    If Not s Is Nothing Then
                        If s.TextColor <> 0 Then
                            Return Color.FromArgb(s.TextColor)
                        End If
                    End If
                    Return AppConfig.ScheduleAppointmentForeColor
            End Select
        End Function


        Private Shared Function ChangeAppPeriod(ByVal start As Integer, ByVal finish As Integer, ByVal app As DataSetAppointments.Appointment, ByVal checkOnly As Boolean) As Boolean
            If (start = 0) And (finish = 0) Then Return False

            If Not checkOnly Then
                Dim m1, m2 As Integer
                Dim h1, h2 As Double
                m1 = start Mod 60
                m2 = finish Mod 60
                h1 = start / 60 - m1 / 60
                h2 = finish / 60 - m2 / 60
                app.StartTime = New Date(app.StartTime.Year, app.StartTime.Month, app.StartTime.Day, h1, m1, 0)
                app.FinishTime = New Date(app.FinishTime.Year, app.FinishTime.Month, app.FinishTime.Day, h2, m2, 0)
                app.ExpectedDuration = finish - start
                Return True
            Else
                If (app.StartTime.TimeOfDay.TotalMinutes <> start) Or (app.FinishTime.TimeOfDay.TotalMinutes <> finish) Then
                    Return False
                Else
                    Return True
                End If
            End If
        End Function

        Public Shared Sub GetSubAppointment(ByVal Start As Integer, ByVal Finish As Integer, ByRef appStart As Integer, ByRef appFinish As Integer, ByVal ext As Boolean)
            If ((appStart = Start) And (appFinish = Finish)) Then
                If ext Then
                    appStart = 0
                    appFinish = 0
                    Return
                Else
                    Return
                End If
            End If

            If appStart = Start Then
                If appFinish > Finish Then
                    If ext Then
                        appStart = Finish
                        appFinish = appFinish
                        Return
                    Else
                        appStart = Start
                        appFinish = Finish
                        Return
                    End If
                Else
                    If ext Then
                        appStart = 0
                        appFinish = 0
                        Return
                    Else
                        Return
                    End If
                End If
            End If

            If appFinish = Finish Then
                If appStart > Start Then
                    If ext Then
                        appStart = 0
                        appFinish = 0
                        Return
                    Else
                        Return
                    End If
                Else
                    If ext Then
                        appStart = appStart
                        appFinish = Start
                        Return
                    Else
                        appStart = Start
                        appFinish = Finish
                        Return
                    End If
                End If
            End If

            If appStart < Start Then
                If appFinish < Start Then
                    If ext Then
                        Return
                    Else
                        appStart = 0
                        appFinish = 0
                        Return
                    End If
                Else
                    If appFinish < Finish Then
                        If ext Then
                            appStart = appStart
                            appFinish = Start
                            Return
                        Else
                            appStart = Start
                            appFinish = appFinish
                            Return
                        End If
                    Else
                        If ext Then
                            appStart = appStart
                            appFinish = Start
                            Return
                        Else
                            appStart = Start
                            appFinish = Finish
                            Return
                        End If
                    End If
                End If
            End If

            If appStart > Finish Then
                If ext Then
                    Return
                Else
                    appStart = 0
                    appFinish = 0
                    Return
                End If
            End If

            If appFinish < Finish Then
                If ext Then
                    appStart = 0
                    appFinish = 0
                    Return
                Else
                    Return
                End If
            Else
                If ext Then
                    appStart = Finish
                    appFinish = appFinish
                    Return
                Else
                    appStart = appStart
                    appFinish = Finish
                    Return
                End If
            End If
        End Sub
#End Region

#Region " Status "
        Public Shared Function GetAppointmentStatus(ByVal app As DataSetAppointments.Appointment) As AppointmentStatus
            If app.RowState = DataRowState.Detached Then
                Return AppointmentStatus.asNew
            End If

            If Not app.IsBillIDNull Then
                Return AppointmentStatus.asPaid
            End If

            If Not app.IsClientIDNull Then
                If AppDataLayer.Clients.FindByClientID(app.ClientID).InOffice = True Then
                    Return AppointmentStatus.asOpen
                End If
            End If

            Return AppointmentStatus.asScheduled
        End Function


        Public Shared Function GetAppointmentStatusString(ByVal app As DataSetAppointments.Appointment) As String
            Return GetAppointmentStatusString(GetAppointmentStatus(app))
        End Function

        Public Shared Function GetAppointmentStatusString(ByVal status As AppointmentStatus) As String
            Select Case status
                Case AppointmentStatus.asNew
                    Return Res.AppointmentStatusNew
                Case AppointmentStatus.asOpen
                    Return Res.AppointmentStatusOpen
                Case AppointmentStatus.asPaid
                    Return Res.AppointmentStatusPaid
                Case AppointmentStatus.asScheduled
                    Return Res.AppointmentStatusScheduled
            End Select
        End Function

#End Region

#Region " Calendar "
        Public Shared Function CreateCalendarAppointment(ByVal dba As DataSetAppointments.Appointment) As Appointment
            Dim ca As New Appointment

            CopyDBAppointment2Calendar(dba, ca)

            Return ca
        End Function

        Public Shared Sub CopyDBAppointment2Calendar(ByVal dbApp As DataSetAppointments.Appointment, ByVal app As Appointment)
            app.Tag = dbApp
            app.DateBegin = dbApp.StartTime
            app.DateEnd = dbApp.FinishTime
            app.Text = AppointmentHelper.GetAppointmentLabel(dbApp)
            app.BackColor = AppointmentHelper.GetAppointmentBackColor(dbApp)
            app.ProgressColor = AppointmentHelper.GetAppointmentBackColor(dbApp)
            app.ForeColor = AppointmentHelper.GetAppointmentForeColor(dbApp)
            app.LineColor = app.ForeColor
        End Sub

        Public Shared Sub CopyCalendarAppointment2DB(ByVal app As Appointment, ByVal dbApp As DataSetAppointments.Appointment)
            dbApp.StartTime = app.DateBegin
            dbApp.FinishTime = app.DateEnd
        End Sub

#End Region

    End Class
End Namespace